/*******************************************************************************
 * (c) Copyright 2007-2018 Microsemi SoC Products Group. All rights reserved.
 * 
 * CoreUARTapb driver implementation. See file "core_uart_apb.h" for a
 * description of the functions implemented in this file.
 * 
 * SVN $Revision: 9746 $
 * SVN $Date: 2018-02-12 17:59:59 +0530 (Mon, 12 Feb 2018) $
 */
#include "../../hal/hal.h"
#include "coreuartapb_regs.h"
#include "core_uart_apb.h"
#include "../../hal/hal_assert.h"

#ifdef __cplusplus
extern "C"
{
#endif

#define NULL_INSTANCE ( ( UART_instance_t* ) 0 )
#define NULL_BUFFER   ( ( uint8_t* ) 0 )

#define MAX_LINE_CONFIG     ( ( uint8_t )( DATA_8_BITS | ODD_PARITY ) )
#define MAX_BAUD_VALUE      ( ( uint16_t )( 0x1FFF ) )
#define STATUS_ERROR_MASK   ( ( uint8_t )( STATUS_PARITYERR_MASK | \
                                           STATUS_OVERFLOW_MASK  | \
                                           STATUS_FRAMERR_MASK ) )
#define BAUDVALUE_LSB ( (uint16_t) (0x00FF) )
#define BAUDVALUE_MSB ( (uint16_t) (0xFF00) )
#define BAUDVALUE_SHIFT ( (uint8_t) (5) )

#define STATUS_ERROR_OFFSET STATUS_PARITYERR_SHIFT 

/***************************************************************************//**
 * UART_init()
 * See "core_uart_apb.h" for details of how to use this function.
 */
void UART_init(UART_instance_t * this_uart, addr_t base_addr,
		uint16_t baud_value, uint8_t line_config)
{
	uint8_t rx_full;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(line_config <= MAX_LINE_CONFIG)
	HAL_ASSERT(baud_value <= MAX_BAUD_VALUE)

	if ((this_uart != NULL_INSTANCE) && (line_config <= MAX_LINE_CONFIG)
			&& (baud_value <= MAX_BAUD_VALUE))
	{
		/*
		 * Store lower 8-bits of baud value in CTRL1.
		 */
		HAL_set_8bit_reg(base_addr, CTRL1, (uint_fast8_t)(baud_value &
		BAUDVALUE_LSB));

		/*
		 * Extract higher 5-bits of baud value and store in higher 5-bits 
		 * of CTRL2, along with line configuration in lower 3 three bits.
		 */
		HAL_set_8bit_reg(base_addr, CTRL2,
				(uint_fast8_t) line_config | (uint_fast8_t)((baud_value &
				BAUDVALUE_MSB) >> BAUDVALUE_SHIFT));

		this_uart->base_address = base_addr;
#ifndef NDEBUG
		{
			uint8_t config;
			uint8_t temp;
			uint16_t baud_val;
			baud_val = HAL_get_8bit_reg(this_uart->base_address, CTRL1);
			config = HAL_get_8bit_reg(this_uart->base_address, CTRL2);
			/*
			 * To resolve operator precedence between & and <<
			 */
			temp = (config & (uint8_t)(CTRL2_BAUDVALUE_MASK));
			baud_val |= (uint16_t)((uint16_t)(temp) << BAUDVALUE_SHIFT);
			config &= (uint8_t)(~CTRL2_BAUDVALUE_MASK);
			HAL_ASSERT(baud_val == baud_value);
			HAL_ASSERT(config == line_config);
		}
#endif

		/*
		 * Flush the receive FIFO of data that may have been received before the
		 * driver was initialized.
		 */
		rx_full = HAL_get_8bit_reg(this_uart->base_address, STATUS)
				& STATUS_RXFULL_MASK;
		while (rx_full)
		{
			HAL_get_8bit_reg(this_uart->base_address, RXDATA);
			rx_full = HAL_get_8bit_reg(this_uart->base_address, STATUS)
					& STATUS_RXFULL_MASK;
		}

		/*
		 * Clear status of the UART instance.
		 */
		this_uart->status = (uint8_t) 0;
	}
}

/***************************************************************************//**
 * UART_send()
 * See "core_uart_apb.h" for details of how to use this function.
 */
uint8_t t_delay = 0;
void UART_send(UART_instance_t * this_uart, const uint8_t * tx_buffer,
		size_t tx_size)
{
	size_t char_idx;
	uint8_t tx_ready;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(tx_buffer != NULL_BUFFER)
	HAL_ASSERT(tx_size > 0)

	if ((this_uart != NULL_INSTANCE) && (tx_buffer != NULL_BUFFER)
			&& (tx_size > (size_t) 0))
	{
		for (char_idx = (size_t) 0; char_idx < tx_size; char_idx++)
		{
			/* Wait for UART to become ready to transmit. */
			do
			{
				tx_ready = HAL_get_8bit_reg(this_uart->base_address, STATUS)
						& STATUS_TXRDY_MASK;
			} while (!tx_ready);
			/* Send next character in the buffer. */
			HAL_set_8bit_reg(this_uart->base_address, TXDATA,
					(uint_fast8_t) tx_buffer[char_idx]);
			for (t_delay = 0; t_delay < 100; t_delay++)
				;
		}
	}
}

/***************************************************************************//**
 * UART_byte_send()
 * See "core_uart_apb.h" for details of how to use this function.
 */
void UART_byte_send(UART_instance_t * this_uart, uint8_t tx_buffer)
{
//	size_t char_idx;
	uint8_t tx_ready;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(tx_buffer != NULL_BUFFER)

	if (this_uart != NULL_INSTANCE)
	{
		/* Wait for UART to become ready to transmit. */
		do
		{
			tx_ready = HAL_get_8bit_reg(this_uart->base_address, STATUS)
					& STATUS_TXRDY_MASK;
		} while (!tx_ready);
		/* Send next character in the buffer. */
		HAL_set_8bit_reg(this_uart->base_address, TXDATA,
				(uint_fast8_t) tx_buffer);
	}
}

/***************************************************************************//**
 * UART_fill_tx_fifo()
 * See "core_uart_apb.h" for details of how to use this function.
 */
size_t UART_fill_tx_fifo(UART_instance_t * this_uart, const uint8_t * tx_buffer,
		size_t tx_size)
{
	uint8_t tx_ready;
	size_t size_sent = 0u;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(tx_buffer != NULL_BUFFER)
	HAL_ASSERT(tx_size > 0)

	/* Fill the UART's Tx FIFO until the FIFO is full or the complete input 
	 * buffer has been written. */
	if ((this_uart != NULL_INSTANCE) && (tx_buffer != NULL_BUFFER)
			&& (tx_size > 0u))
	{
		tx_ready = HAL_get_8bit_reg(this_uart->base_address, STATUS)
				& STATUS_TXRDY_MASK;
		if (tx_ready)
		{
			do
			{
				HAL_set_8bit_reg(this_uart->base_address, TXDATA,
						(uint_fast8_t) tx_buffer[size_sent]);
				size_sent++;
				tx_ready = HAL_get_8bit_reg(this_uart->base_address, STATUS)
						& STATUS_TXRDY_MASK;
			} while ((tx_ready) && (size_sent < tx_size));
		}
	}
	return size_sent;
}

/***************************************************************************//**
 * UART_get_rx()
 * See "core_uart_apb.h" for details of how to use this function.
 */
size_t UART_get_rx(UART_instance_t * this_uart, uint8_t * rx_buffer,
		size_t buff_size)
{
	uint8_t new_status;
	uint8_t rx_full;
	size_t rx_idx = 0u;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(rx_buffer != NULL_BUFFER)
	HAL_ASSERT(buff_size > 0)

	if ((this_uart != NULL_INSTANCE) && (rx_buffer != NULL_BUFFER)
			&& (buff_size > 0u))
	{
		rx_idx = 0u;
		new_status = HAL_get_8bit_reg(this_uart->base_address, STATUS);
		this_uart->status |= new_status;
		rx_full = new_status & STATUS_RXFULL_MASK;
		while ((rx_full) && (rx_idx < buff_size))
		{
			rx_buffer[rx_idx] = HAL_get_8bit_reg(this_uart->base_address,
					RXDATA);
			rx_idx++;
			new_status = HAL_get_8bit_reg(this_uart->base_address, STATUS);
			this_uart->status |= new_status;
			rx_full = new_status & STATUS_RXFULL_MASK;
		}
	}
	return rx_idx;
}

/***************************************************************************//**
 * UART_polled_tx_string()
 * See "core_uart_apb.h" for details of how to use this function.
 */
void UART_polled_tx_string(UART_instance_t * this_uart,
		const uint8_t * p_sz_string)
{
	uint32_t char_idx;
	uint8_t tx_ready;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	HAL_ASSERT(p_sz_string != NULL_BUFFER)

	if ((this_uart != NULL_INSTANCE) && (p_sz_string != NULL_BUFFER))
	{
		char_idx = 0U;
		while (0U != p_sz_string[char_idx])
		{
			/* Wait for UART to become ready to transmit. */
			do
			{
				tx_ready = HAL_get_8bit_reg(this_uart->base_address, STATUS)
						& STATUS_TXRDY_MASK;
			} while (!tx_ready);
			/* Send next character in the buffer. */
			HAL_set_8bit_reg(this_uart->base_address, TXDATA,
					(uint_fast8_t) p_sz_string[char_idx]);
			char_idx++;
		}
	}
}

/***************************************************************************//**
 * UART_get_rx_status()
 * See "core_uart_apb.h" for details of how to use this function.
 */
uint8_t UART_get_rx_status(UART_instance_t * this_uart)
{
	uint8_t status = UART_APB_INVALID_PARAM;

	HAL_ASSERT(this_uart != NULL_INSTANCE)
	/*
	 * Extract UART error status and place in lower bits of "status".
	 * Bit 0 - Parity error status
	 * Bit 1 - Overflow error status
	 * Bit 2 - Frame error status
	 */
	if (this_uart != NULL_INSTANCE)
	{
		status = ((this_uart->status & STATUS_ERROR_MASK) >>
		STATUS_ERROR_OFFSET);
		/*
		 * Clear the sticky status for this instance.
		 */
		this_uart->status = (uint8_t) 0;
	}
	return status;
}

#ifdef __cplusplus
}
#endif
